package org.NooLab.itexx;

import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.io.IOException;

import javax.swing.JFrame;

import com.cybozu.labs.langdetect.LangDetectException;


import org.NooLab.utilities.clipboard.CBoardProperties;

import org.NooLab.utilities.clipboard.ClipBoardIntf;
import org.NooLab.utilities.clipboard.ClipBoardListener;
import org.NooLab.utilities.logging.PrintLog;
import org.NooLab.utilities.strings.StringsUtil;
import org.NooLab.utilities.timing.DelayNotificationIntf;


import org.NooLab.itexx.app.config.ITexxAppPropertiesIntf;
import org.NooLab.itexx.app.config.ITexxIniPreferences;
import org.NooLab.itexx.app.controller.ClipboardConsumerIntf;
import org.NooLab.itexx.app.controller.Documentary;
import org.NooLab.itexx.app.controller.ExitActionProviderIntf;
import org.NooLab.itexx.app.controller.FoundResultsGuiUpdater;
import org.NooLab.itexx.app.controller.HtmlConsumer;
import org.NooLab.itexx.app.controller.HtmlConsumerIntf;
import org.NooLab.itexx.app.controller.ItexxModelController;
import org.NooLab.itexx.app.controller.PageLoader;
import org.NooLab.itexx.app.controller.PreferencesHandler;
import org.NooLab.itexx.app.controller.SessionManager;
import org.NooLab.itexx.app.controller.StatusMessage;
import org.NooLab.itexx.app.controller.analyzer.AnalyticsPort;
import org.NooLab.itexx.app.controller.content.SearchEngines;
import org.NooLab.itexx.app.controller.results.SearchEngineResults;
import org.NooLab.itexx.app.gui.DialogMaster;
import org.NooLab.itexx.app.gui.StackedPanelProducer;
import org.NooLab.itexx.app.gui.definition.ITexxWindowGuiIntf;
import org.NooLab.itexx.app.gui.handlers.ITexxGuiUpdater;
import org.NooLab.itexx.app.gui.handlers.ResultsDisplayAgent;
import org.NooLab.itexx.app.gui.handlers.WindowAppearance;
import org.NooLab.itexx.app.gui.handlers.WindowAppearanceIntf;
import org.NooLab.itexx.app.gui.handlers.WindowState;
import org.NooLab.itexx.app.gui.handlers.keys.ItexxGuiKeyListener;
import org.NooLab.itexx.app.gui.handlers.keys.Keyhandlers;
import org.NooLab.itexx.app.processes.LinguisticsPort;
import org.NooLab.itexx.app.processes.ProcessAdmin;
import org.NooLab.itexx.app.storage.GuiPersistence;
import org.NooLab.itexx.browser.BrowserHostList;
import org.NooLab.itexx.retrieval.LanguageDetector;
import org.NooLab.itexx.retrieval.RequestDispatcher;
import org.NooLab.itexx.storage.StorageProvider;
import org.NooLab.itexx.stuctures.GuiPersistenceData;

// import org.NooLab.itexx.retrieval.ResultsDigester;




public class ITexxWindowCommons implements 		DelayNotificationIntf,
												ExitActionProviderIntf {

	public static final int _DIAGNOSIS = 1;
	
	
	ITexxWindowIntf app;
	ITexxAppPropertiesIntf appProperties;
	private ITexxWindowCommons _commons;
	
	BrowserHostList hostList;
	private JFrame frame;
	private ITexxWindowGuiIntf windowGui;
	private StackedPanelProducer stackedPanelProducer;

	ITexxGuiUpdater guiUpdater;
	GuiPersistence guiPersistence;

	StorageProvider storageProvider;

	ProcessAdmin processAdmin;
	LinguisticsPort linguisticsPort;

	AnalyticsPort analyticsPort;
	// -----------------------------------------------------
	private TemporaryContextClipboard contextClipboard;
	private ClipBoardListener clipboard;
	String lastClipboardContent = "";
	// -----------------------------------------------------

	private ClipboardConsumerIntf consumer;
	private WindowAppearanceIntf windowAppearance, windowState, resultsDisplayAgent;
	private HtmlConsumer htmlConsumer = null;
	private ItexxModelController modelController;
	private SessionManager sessionManager;
	
	private boolean firstStart=false; 
	// detected by AttendingAppProperties, which is checking for the window properties file
	
	private boolean guiWorkerIsWorking;
	private StackedPanelProducer panelProducer;

	RequestDispatcher requestDispatcher;
	PageLoader pageLoader;
	FoundResultsGuiUpdater resultsGuiUpdater;
	@SuppressWarnings("unused")
	private Documentary documentary;
	
	SearchEngines searchEngines;
	
	private LanguageDetector languageDetector;
	private boolean applicationIsStopping = false;
	private boolean focusInputHasChanged;

	private Keyhandlers keyHandlers;

	public StringsUtil strgutil = new StringsUtil();
	public PrintLog out = new PrintLog(2, true);
	private int maxPanelsCount;


	private SearchEngineResults searchEngineResults;
	
	
	// ========================================================================
	private ITexxWindowCommons(ITexxWindowIntf app) {
		this.app = app;
		_commons = this;
		out.setPrefix("[ITEXX]");
		
		searchEngines = new SearchEngines(this); 
	}

	public static ITexxWindowCommons getInstance(ITexxWindowIntf app) {

		ITexxWindowCommons _commons = new ITexxWindowCommons(app);

		return _commons;
	}
	
	public void setReferences(){
		
		// appProperties = new ITexxAppProperties() ;

		windowGui = app.getWindowGui();
		app.setCommons(this);

		
		
		
		sessionManager = new SessionManager(app); // cares for its own storage
													// object

		setModelController( windowGui.getItexxModel());
		requestDispatcher = new RequestDispatcher(app);

		linguisticsPort = new LinguisticsPort(this, requestDispatcher);
 
		maxPanelsCount = this.getAppProperties().getIniPreferences().getPanelsMaxNumber();
		resultsGuiUpdater = new FoundResultsGuiUpdater(this, windowGui, maxPanelsCount); 

		
	}

	
	// ========================================================================

	// for delayed instantiation... it needs a reference to the properties
	public void createProcessAdmin() {

		processAdmin = new ProcessAdmin(this);
		pageLoader = new PageLoader(this) ; // after the session manager, after the ProcessAdmin !
	}

	/*
	 * public static ITexxWindowCommons getInstance(ITexxWindow2 app) {
	 * ITexxWindowCommons _commons = new ITexxWindowCommons(app); return
	 * _commons; }
	 */

	public void close() {
		applicationIsStopping = true;

		try{

			AWTEventListener[] awtL = Toolkit.getDefaultToolkit().getAWTEventListeners();
			
			if ((awtL != null) && (awtL.length > 0)) {
				for (int i = 0; i < awtL.length; i++) {
					Toolkit.getDefaultToolkit().removeAWTEventListener(awtL[i]);
				}
			}
	
		}catch(Exception e){}

		// now calling close() for all object that run some enclosed thread
		if (pageLoader!=null)    pageLoader.close();
		if(requestDispatcher!=null)requestDispatcher.close();
		if(processAdmin!=null)   processAdmin.close();
		if(sessionManager!=null) sessionManager.close();
		if(guiUpdater!=null)	 guiUpdater.close();
		if (resultsGuiUpdater!=null) resultsGuiUpdater.close() ;
		
		if(modelController!=null)modelController.getSearchRequest().close();
		if (out!=null)out.delay(50);
		
		// TODO: any other threaded process to close ???
		if (guiPersistence!=null)guiPersistence.perform(app);
		
		if (out!=null)out.delay(250);
	}

	// from ExitActionProviderIntf
	@Override
	public int perform(ITexxWindowIntf app) {
		//
		close();
		
		return 0;
	}

	public void showInfo(int page) {
		Documentary.showBasicInfo(appProperties);
	}

	public void showPreferences(){
	
		try {
			(new PreferencesHandler(_commons)).openPreferencesView();
		} catch (IOException e) {
			out.printErr(2, "Problem met whle trying to open the preferences file : "+e.getMessage());
		}
	}

	public void initGuiPersistence() {
		// we have to do it extra, since we it needs a cross-reference
		// between commons and app
		guiPersistence = new GuiPersistence(app, windowGui);

		storageProvider = new StorageProvider(app);
		
		guiUpdater = new ITexxGuiUpdater(windowGui);

		analyticsPort = new AnalyticsPort( this )  ;
		
		documentary = new Documentary();
	}

	public void setGuiData(ITexxAppPropertiesIntf appProperties) {
		// where does it come from?
		String str;

		guiPersistence.setGuiData(appProperties.getGuiData());

		GuiPersistenceData gd = guiPersistence.getGuiData();

		if (gd == null) {
			//
			if (gd == null) {
				gd = new GuiPersistenceData();

				appProperties.setGuiData(gd);
				guiPersistence.setGuiData(gd);

				appProperties.save();
			}
		}

		str = gd.getFocusNormal();
		windowGui.getTextNormal().setText(str);

		str = gd.getFocusMandatory();
		windowGui.getTextMandatory().setText(str);

		str = gd.getFocusExcluding();
		windowGui.getTextExclude().setText(str);

		str = gd.getFocusTolerated();
		windowGui.getTextTolerated().setText(str);

		str = gd.getFocusNormalFilename();
		windowGui.getFocusNormalTextAreaFileDrop().setText(str);

		// also load the request history, and then create the panels of the last
		// results view
	}
	
	public void loadSearchEngineDefinitions() {
		ITexxIniPreferences iniPref ;
		
		iniPref = appProperties.getIniPreferences();
		iniPref.loadSearchEngineDefinitions( searchEngines ) ;
		
	}


	public void prepareLanguageDetector() {

		try {

			languageDetector = new LanguageDetector(this);

			appProperties.setConsolidateLingWordSets(true);

		} catch (LangDetectException e) {
			e.printStackTrace();
		}

	}

	public LanguageDetector getLanguageDetector() {
		//
		return languageDetector;
	}

	public void setBrowserHostList(BrowserHostList hostlist) {
		//
		hostList = hostlist;
	}

	public BrowserHostList getHostList() {
		return hostList;
	}

	public ITexxWindowIntf getApp() {
		return app;
	}

	public ITexxAppPropertiesIntf getAppProperties() {
		return appProperties;
	}

	public void setFrameReference(JFrame frame) {

		this.frame = frame;

	}

	public JFrame getFrame() {
		return frame;
	}

	public void setWindowGui(ITexxWindowGuiIntf windowgui) {
		windowGui = windowgui;
		windowGui.setAppCommons(this);

		if (windowAppearance == null) {
			windowAppearance = (WindowAppearanceIntf) new WindowAppearance(
					windowGui);
		}
		if (windowState == null) {
			windowState = (WindowAppearanceIntf) new WindowState(windowGui);
		}
		
		if (resultsDisplayAgent==null){
			resultsDisplayAgent = (WindowAppearanceIntf) new ResultsDisplayAgent(windowGui);
		}
	}

	public ITexxWindowGuiIntf getWindowGui() {
		return windowGui;
	}

	public HtmlConsumerIntf getHtmlConsumer() {
		//
		return htmlConsumer;
	}

	public HtmlConsumerIntf createHtmlConsumer() {

		htmlConsumer = new HtmlConsumer(app);
		return htmlConsumer;
	}

	public void setStackedPanelProducer(StackedPanelProducer panelProducer) {

		stackedPanelProducer = panelProducer;
	}

	public StackedPanelProducer getStackedPanelProducer() {
		return stackedPanelProducer;
	}

	public void activateClipboard(int duration) {
		//
		
		if (duration<0){
			
			new TemporaryContextClipboard(duration);
			return;
		}
		
		duration = 1;
		String str = "clipboard is now active for "
				+ ((int) ((double) duration / 1000.0)) + " seconds...";

		windowGui.getOut().print(2,
				"Now listening to clipboard for " + duration + " seconds...");
		(new StatusMessage(windowGui, str, -1)).show();

		if (contextClipboard != null) {
			contextClipboard.stop();
		}
		contextClipboard = new TemporaryContextClipboard(duration);

		// just to be sure that it stops
		// (new DelayFor(this)).period( duration*1000, 1000);
	}

	protected void clipboardIsClosing() {
		
		String str = "clipboard has been inactivated.";
		contextClipboard.stop();
		(new StatusMessage(windowGui, str, 20000)).show();
	}

	/**
	 * typically a message digester that works as a preprocessor for the search;
	 * this preprocessor perform tf-itf, doublet or som-encoding, dependent on
	 * availability
	 */
	public void setClipboardConsumer(ClipboardConsumerIntf consumer) {
		this.consumer = consumer;
	}

	// ------------------------------------------------------------------------
	class TemporaryContextClipboard implements ClipBoardIntf {

		long readerStartupTime = System.currentTimeMillis();
		long eventTime = 0L;
		private CBoardProperties cbProps;

		// ................................................
		public TemporaryContextClipboard() {
			init(-1);
		}

		public TemporaryContextClipboard(int duration) {
			
			init(duration);
		}

		private void init(int duration) {

			cbProps = ClipBoardListener.getProperties();

			cbProps.setAllowForFetchOnStart(true);

			if (duration<0){
				cbProps.setNilAction(true);
			}
			
			clipboard = ClipBoardListener.getInstance(this, cbProps); 
			// this == callinginstance == ClipBoardIntf

			if (duration < 0){
				// clipboard.setNextActionBlocked(true);
				clipboard.clearClipboard();
				clipboard.stopListening();
				return;
			}
			
			if (duration > 0) {
				if (duration < 500) {
					duration = duration * 1000;
				}
				clipboard.setTimeOut(duration);
			}
			clipboard.start();

		}

		// ................................................

		@Override
		public void clipboardContentStrEvent(String str) {
			// measuring time allows to identify a user-based action from
			// initiated retrieval
			// of content that already was in the clipboard when we started the
			// reader

			long dt;
			eventTime = System.currentTimeMillis();
			dt = eventTime - this.readerStartupTime;

			lastClipboardContent = str;
			windowGui.getOut().printErr( 2, "Content received from clipboard :  " + strgutil.trunk(str,30)); 
											
			windowGui.getOut().print(3, " ... after dt=" + dt + "ms ...");

			if (consumer != null) {
				consumer.setClipboardString(lastClipboardContent);
				// actually, it should be an inputstream, since later we will
				// have to deal with images too !
			}
			if ((dt > 350) && (str.trim().length() > 0)) {

				windowGui.getOut().print(2,"Listening to clipboard stopped after receiving text (len:"
											+ str.trim().length() + ")");// (dt="+dt+")." );

				if (clipboard != null) {
					clipboard.stopListening();
				}
				clipboard = null;
			} else {
				// dependent on option ?
				if (dt>11000){
					clipboard.stopListening();
				}
			}
		}

		@Override
		public void clipboardContentImgEvent(Object obj) {
			// TODO Auto-generated method stub
			try{
				// might not be an image
				
			}catch(Exception e){
				
			}
		}

		public void stop() {
			//
			if (clipboard != null) {
				clipboard.stopListening();
				// ABC789 clipboard = null;
			}
		}

		@Override
		public void clipboardProcessStopped(boolean alive) {
			if (clipboard != null) {
				clipboard.stopListening();
			}
			// ABC789 clipboard = null;
		}

	}

	@Override
	public void setPercentageOfWaitingTime(double perc) {
		if ((clipboard != null) && (perc > 99.0)) {
			windowGui.getOut().print(2, "Listening to clipboard stopped.");
			clipboard.stopListening();
			clipboard = null;
		}
	}

	public void toggleAppWindowState(boolean isSimple) {

		windowGui.getOut().print(2, "toggling window appearance.");

		windowAppearance.toggleSimpleView(isSimple);

	}

	public void toggleAppWindowState() {
		//
		windowGui.getOut().print(2, "toggling window appearance.");

		windowAppearance.toggleSimpleView(2);
	}

	public boolean isEarlyPreprocessing() {
		//
		boolean rB = appProperties.isEarlyPreprocessing();

		if (rB) {

		}
		return rB;
	}

	public boolean isEarlyPreprocessing(String urlstr) {
		//
		boolean rB = appProperties.isEarlyPreprocessing();

		if ((rB) && (urlstr.length() == 0)) {
			rB = false;
		}
		if (rB) {
			rB = urlstr.toLowerCase().contains(".google.");
			if (rB == false) {
				rB = urlstr.toLowerCase().contains(".bing.");
			}
			if (rB == false) {
				rB = urlstr.toLowerCase().contains(".yahoo.");
			}
		}
		return rB;
	}

	public void setModelController(ItexxModelController modelController) {
		// this object is owned by the Gui component... important, if we change
		// it!
		this.modelController = modelController;
	}

	public ItexxModelController getModelController() {
		return modelController;
	}

	public SessionManager getSessionManager() {
		//
		return sessionManager;
	}

	public JFrame getMainFrame() {
		//

		JFrame frame = app.getMainFrame();// (new GuiWorker(
											// app,true,1f)).getFrame();
		return frame;
	}

	public void setGuiWorkerIsWorking(boolean flag) {
		guiWorkerIsWorking = flag;

	}

	public boolean getGuiWorkerIsWorking() {
		return guiWorkerIsWorking;
	}

	public void setPanelProducer(StackedPanelProducer panelproducer) {
		panelProducer = panelproducer;
	}

	public StackedPanelProducer getPanelProducer() {
		return panelProducer;
	}

	public ProcessAdmin getProcessAdmin() {
		return processAdmin;
	}

	public PageLoader getPageLoader() {
		return pageLoader;
	}

	public PageLoader getNativeBrowserPort() {
		
		return pageLoader;
	}
	public void setNativeBrowserPort(PageLoader pageloader) {
		pageLoader = pageloader ;
	}

	public LinguisticsPort getLinguisticsPort() {
		if (linguisticsPort ==null){
			linguisticsPort = new LinguisticsPort(this, requestDispatcher);
		}
		if (linguisticsPort.countObservers()==0){
			linguisticsPort.addObserver(requestDispatcher);
		}
		return linguisticsPort;
	}
	
	public LinguisticsPort getLinguisticsPort( RequestDispatcher requestDispatcher) {
		//
		linguisticsPort = requestDispatcher.getLinguisticsPort() ;
		if (linguisticsPort.countObservers()==0){
			linguisticsPort.addObserver(requestDispatcher);
		}

		return linguisticsPort;
	}

	public StorageProvider getStorageProvider() {
		return storageProvider;
	}
	
	public void setWindowAppearanceEngine(WindowAppearanceIntf windowappearance) {
		//
		windowAppearance = windowappearance;
	}
	
	public void setResultsDisplayAgent(WindowAppearanceIntf displayAgent) {
		// 
		resultsDisplayAgent = displayAgent;
	}

	public WindowAppearanceIntf setResultsDisplayAgent() {
		// 
		return resultsDisplayAgent ;
	}

	public FoundResultsGuiUpdater getResultsGuiUpdater() {
		return resultsGuiUpdater;
	}

	public WindowAppearanceIntf getWindowAppearanceEngine() {
		//
		return windowAppearance;
	}

	public WindowAppearanceIntf getWindowStateEngine() {
		//
		return windowState;
	}

	public void getWindowStateEngine(WindowAppearanceIntf stateEngine) {
		//
		windowState = stateEngine;
	}

	public RequestDispatcher getRequestDispatcher() {
		return requestDispatcher;
	}

	public GuiPersistence getGuiPersistenceObject() {
		return guiPersistence;
	}

	public void setProperties(ITexxAppPropertiesIntf properties) {
		appProperties = properties;
		appProperties.setCommonsReference(this);
	}

	public StorageProvider getStorage() {
		// TODO Auto-generated method stub
		return storageProvider;
	}

	public SearchEngines getSearchEngines() {
		return searchEngines;
	}

	public int getMaxPanelsCount() {
		return maxPanelsCount;
	}

	public void establishKeyListener(ItexxGuiKeyListener appKeyListener) {
		//

		keyHandlers = new Keyhandlers(windowGui);

		int r;
		// just for transporting the object "in context"
		keyHandlers.setAppKeyListener(appKeyListener);

		keyHandlers.getGlobalKeyActionCodeMapping().put("f1", 0);
		// setting up the global action keys
		// keyHandlers.getGlobalKeyActionCodeMapping().put("ctrl-w",2);
		keyHandlers.getGlobalKeyActionCodeMapping().put("alt-d", 1); 
		// just transparency, only effective for simple window, not used if windowGui is complete
		keyHandlers.getGlobalKeyActionCodeMapping().put("alt-w", 101);
		keyHandlers.getGlobalKeyActionCodeMapping().put("alt-c", 30); // remove all panels
		keyHandlers.getGlobalKeyActionCodeMapping().put("alt-r", 32); // remove selected panel
		keyHandlers.getGlobalKeyActionCodeMapping().put("alt-h", 40);
		
		keyHandlers.getGlobalKeyActionCodeMapping().put("ctrl-x", 999);

		
		// binding controls to those keys
		// Keyhandler frameKeys = keyHandlers.create(1);
		keyHandlers.create(1).attachGlobalKeys(frame, windowAppearance, resultsDisplayAgent); 
		// this interface must just provide a "perform(int)"

		
		/*
		 * individual key mappings could be done like so... -> it requires to
		 * keep a variable that is provided by "create()"
		 * frameKeys.addAction("ctrl-d", (KeyActionIntf)(new
		 * WindowToggler(windowAppearance,1) ) ); // 1 = arbitrary parameter,
		 * later a param object!! untyped array or the like
		 * frameKeys.addAction("ctrl-w", (KeyActionIntf)(new
		 * WindowToggler(windowAppearance,2) ) ); r = appKeyListener.add( frame,
		 * 1, frameKeys);
		 */

		keyHandlers.create(2).attachGlobalKeys(windowGui.getTopPanel(), windowAppearance, resultsDisplayAgent);

		keyHandlers.create(3).attachGlobalKeys(windowGui.getPanelFound(), windowAppearance,resultsDisplayAgent);
		/*
		 * panelFoundKeys.addAction("ctrl-d", (KeyActionIntf)(new
		 * WindowToggler(windowAppearance,1) ) ); // 1 = arbitrary parameter,
		 * later a param object!! untyped array or the like
		 * panelFoundKeys.addAction("ctrl-w", (KeyActionIntf)(new
		 * WindowToggler(windowAppearance,2) ) ); appKeyListener.add(
		 * PanelFound, 5,panelFoundKeys);
		 */

		keyHandlers.create(4).attachGlobalKeys( windowGui.getjPanelScrollWrapFoundStack(), windowAppearance,resultsDisplayAgent);

		/*
		 * scrollStackKeys.addAction("ctrl-d", (KeyActionIntf)(new
		 * WindowToggler(windowAppearance,1) ) ); // 1 = arbitrary parameter,
		 * later a param object!! untyped array or the like
		 * scrollStackKeys.addAction("ctrl-w", (KeyActionIntf)(new
		 * WindowToggler(windowAppearance,2) ) ); appKeyListener.add(
		 * jPanelScrollWrapFoundStack, 5,scrollStackKeys);
		 */

	}

	public Keyhandlers getKeyHandlers() {
		return keyHandlers;
	}

	public void setInformationAboutSubProcess(int index, int state, String guid) {
		// internal class ProcessSupervisor in:
		ProcessAdmin pa; // will stop the app after 20 seconds

		if ((state < 0) && (applicationIsStopping==false)){
			// show dialog box
			out.printErr(1,"critical error, subprocess did not start successfully. ");
		}

	}



	public boolean isApplicationIsStopping() {
		//
		return applicationIsStopping;
	}

	public void setFocusInputHasChanged(boolean flag) {
		focusInputHasChanged = flag;
	}

	public boolean isFocusInputHasChanged() {
		return focusInputHasChanged;
	}

	public boolean getFocusInputHasChanged() {
		return focusInputHasChanged;
	}

	public AnalyticsPort getAnalyticsPort() {
		// 
		return analyticsPort;
	}

	public void setFirstStart(boolean flag) {
		// 
		firstStart = flag;
 	}

	public boolean isFirstStart() {
		return firstStart;
	}

	public DialogMaster getDialogMaster() {
		// 
		return new DialogMaster(this);
	}

	public void setSearchEngineResults(SearchEngineResults sers) {
		// just the reference
		searchEngineResults = sers;
	}

	public SearchEngineResults getSearchEngineResults() {
		return searchEngineResults;
	}



	

}
